from google.appengine.ext import db
from model import *
class TableRecord(object):
    """abastract table record class which automatically track its changes"""
    # 0.11 compatibility
    #use the data_dictionary for pre-defined fields
    #but it does accept new fields by doing TableRecord['new field']
    time_created = property(lambda self: self.values.get('ctime'))
    time_changed = property(lambda self: self.values.get('mtime'))
    
    def __init__(self, key=None, fields=[], pfields=[], tfields=[]):
        self.fields = fields
        self.protected_fields = pfields
        self.time_fields = tfields
        
        self.values = {} #keep all the value in it
        if key is not None:
            self._fetch_record(key)
        else:
            self._init_defaults()
            self.id = None
        self._old = {}#keep old value


    exists = property(fget=lambda self: self.id is not None)

    def _init_defaults(self):
        for field in self.fields:
            default = None
            if not field in self.protected_fields:
                self.values.setdefault(field, default)

    def _fetch_record(self, key):
        row = db.get(key)
        if not row:
            print 'Not exist'
            self.id = None
            return

        self.id = key
        for fld in self.fields:
            if not fld in self.protected_fields:
                
                if hasattr(row, fld):
                    self.values[fld]= getattr(row, fld)
                else:
                    self.values[fld]= None

    def __getitem__(self, name):
        return self.values.get(name)

    def __setitem__(self, name, value):
        """Log ticket modifications so the table ticket_change can be updated
        """
        if name in self.values and self.values[name] == value:
            return
        if name in self.protected_fields:
            return
        if name not in self._old: # Changed field
            #if it is a new value, return None
            self._old[name] = self.values.get(name)
        elif self._old[name] == value: # Change of field reverted
            del self._old[name]
        self.values[name] = value

    def get_value_or_default(self, name):
        """Return the value of a field or the default value if it is undefined
        """
        try:
            value = self.values[name]
            return value
        except KeyError:
            pass
        
    def populate(self, values):
        """Populate the ticket with 'suitable' values from a dictionary"""
        for fld in self.fields:
            self[fld] = values.get(fld, None)


    def insert(self, model):
        """Add ticket to database.
        
        The `db` argument is deprecated in favor of `with_transaction()`.
        """
        assert not self.exists, 'Cannot insert an existing ticket'

        # Add a timestamp
        # Perform type conversions
        values = dict(self.values)
        for field in self.time_fields:
            if field in values:
                pass
                #values[field] = values[field]#do the conversion
        

        #def do_insert():
        t = model()
        for k, v in self.values.items():
            setattr(t, k, v)
        
        t.put()
        self.id = str(t.key())
        self._old = {}

#        for listener in TicketSystem(self.env).change_listeners:
#            listener.ticket_created(self)

        return self.id

    def save_changes(self):
        """
        Store ticket changes in the database. The ticket must already exist in
        the database.  Returns False if there were no changes to save, True
        otherwise.
        
        The `db` argument is deprecated in favor of `with_transaction()`.
        """
        assert self.exists, 'Cannot update a new ticket'

        if not self._old:
            return False # Not modified

        t = db.get(self.id)
        for k, v in self.values.items():
            if k in self.protected_fields:
                pass
            else:
                if k=='description':
                    v = db.Text(v)
                setattr(t, k, v)
        t.put()
        
        #save the change log
        for name in self._old.keys():
            change_record = TrackChange(field_name=name, record_ref=t)
            change_record.oldvalue = self._old[name]
            change_record.newvalue = self.values[name]
            change_record.put()
        old_values = self._old
        self._old = {}
        self.values['changetime'] = t.ctime

    def delete(self):
        self['record_status'] = 'delete'
        self.save_changes()